Skip to content

feat: scaffold OpenFeature feature-flag support#263

Merged
devantler merged 3 commits into
mainfrom
claude/featureflag-openfeature-scaffold
Jul 7, 2026
Merged

feat: scaffold OpenFeature feature-flag support#263
devantler merged 3 commits into
mainfrom
claude/featureflag-openfeature-scaffold

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Daily AI Assistant

Why

Projects generated from the .NET template start with no way to land a feature behind a flag — so deploy and release are coupled and rollback means a redeploy. This is the .NET stack's slice of the portfolio-wide feature-flag-first standard.

What

Adds a portable OpenFeature scaffold to the template: an example flag default-off, a gated example path, and tests covering both states (on/off/unset). Call sites stay provider-agnostic; the scaffold ships an in-memory provider so it evaluates with no backend, swappable for flagd or another provider later.

Design note (for promotion): the issue proposed the Microsoft.FeatureManagement OpenFeature bridge, but that package is preview-only today and the template's policy is to avoid preview dependencies — so this ships the GA in-memory provider (consistent with the Go template) and documents adopting the MS bridge once it ships GA. Redirect here if you'd prefer the preview bridge instead.

Fixes #262 · Part of devantler-tech/monorepo#2059

Adds a portable OpenFeature feature-flag scaffold (FeatureFlags.cs) with an
example flag default-off, gated example path, and both-state tests, so features
generated from the template land behind a flag by default.

Ships OpenFeature's built-in in-memory provider (evaluates with no backend);
the Microsoft.FeatureManagement OpenFeature bridge is preview-only, so the
scaffold does not depend on it (the template avoids preview packages).

Fixes #262

> 🤖 Generated by the Daily AI Assistant

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an OpenFeature-based feature-flag scaffold to the template. A new FeatureFlags static class in src/Example/FeatureFlags.cs defines an example boolean flag with an in-memory provider, client creation, and description helper. The Example.csproj adds an OpenFeature package reference. Unit tests validate default-off, on, and unset flag resolution. The rename scripts now update and verify FeatureFlags.cs content, and AGENTS.md, README.md, and .editorconfig are updated to document the feature-flag conventions and disable CA2007 for tests.

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: adding OpenFeature-based feature-flag scaffolding.
Description check ✅ Passed The description is directly about the same OpenFeature feature-flag scaffold and test coverage.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/rename-placeholders.sh`:
- Around line 127-129: The rename workflow already repoints
src/$new_name/FeatureFlags.cs, but the rename test does not cover it. Update
scripts/rename-placeholders.test.sh to assert that FeatureFlags.cs is renamed
and its contents are repointed to the new scaffold, using the existing rename
assertions in the test suite so this path stays covered alongside the other
moved files.

In `@src/Example/FeatureFlags.cs`:
- Around line 53-58: CreateClientAsync is mutating the process-wide Api.Instance
provider on every call, so previously created clients can silently switch
providers. Update the API documentation/comments on CreateClientAsync (and the
provider parameter if needed) to warn that it should be called once at startup,
since SetProviderAsync re-registers the global default provider and affects all
clients returned by GetClient. If multiple independent providers are needed
later, direct callers toward domain/named client patterns instead of repeated
CreateClientAsync calls.

In `@tests/Example.Tests/ExampleClassTests.cs`:
- Around line 97-112: BuildExampleProvider duplicates the same example-flag
variant mapping used by FeatureFlags.CreateInMemoryProvider, so it can drift if
the variant strings change. Update this test helper to reuse the production
variant constants from FeatureFlags instead of hardcoding "on" and "off", while
keeping the same InMemoryProvider, Flag<bool>, and ExampleFeature setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ef3d6f65-973c-430c-a41d-631f42540319

📥 Commits

Reviewing files that changed from the base of the PR and between 1b639d3 and c346106.

📒 Files selected for processing (7)
  • .editorconfig
  • AGENTS.md
  • README.md
  • scripts/rename-placeholders.sh
  • src/Example/Example.csproj
  • src/Example/FeatureFlags.cs
  • tests/Example.Tests/ExampleClassTests.cs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • devantler-tech/actions (auto-detected)
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
{src/Example/Example.csproj,tests/Example.Tests/Example.Tests.csproj}

📄 CodeRabbit inference engine (AGENTS.md)

Keep every net10.0 target-framework declaration updated together in the library and test project files; never leave one project on an old TFM when bumping the scaffold.

Files:

  • src/Example/Example.csproj
{scripts/rename-placeholders.sh,scripts/rename-placeholders.test.sh,src/Example/**/*.cs,tests/Example.Tests/**/*.cs}

📄 CodeRabbit inference engine (AGENTS.md)

Keep the onboarding rename script and its test in lockstep; if a placeholder .cs scaffold file is added or changed, update the rename script substitutions accordingly.

Files:

  • scripts/rename-placeholders.sh
  • src/Example/FeatureFlags.cs
  • tests/Example.Tests/ExampleClassTests.cs
{src/**/*.cs,tests/**/*.cs}

📄 CodeRabbit inference engine (AGENTS.md)

{src/**/*.cs,tests/**/*.cs}: New features must be introduced behind an OpenFeature flag, default-off, and code paths should evaluate flags through IFeatureClient (via the FeatureFlags helper) rather than a provider directly.
Short-lived release flags must be removed after rollout; long-lived ops/permission flags are the exception, and trivial/mechanical changes are exempt from flagging.

Files:

  • src/Example/FeatureFlags.cs
  • tests/Example.Tests/ExampleClassTests.cs
src/Example/FeatureFlags.cs

📄 CodeRabbit inference engine (AGENTS.md)

The scaffold’s OpenFeature setup should use the built-in in-memory provider from FeatureFlags.CreateInMemoryProvider; do not depend on a real backend or preview bridge in the template.

Files:

  • src/Example/FeatureFlags.cs
tests/Example.Tests/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Feature-flagged behavior must be tested in both states (on and off), and unset/default behavior should be covered as default-off.

Files:

  • tests/Example.Tests/ExampleClassTests.cs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: devantler-tech/dotnet-template

Timestamp: 2026-07-06T19:49:46.993Z
Learning: Validate locally with `dotnet build` and `dotnet test` before opening a PR; warnings are treated as errors.
Learnt from: CR
Repo: devantler-tech/dotnet-template

Timestamp: 2026-07-06T19:49:46.993Z
Learning: Begin every PR, issue, or comment with `> 🤖 Generated by the Daily AI Assistant` when acting as the autonomous assistant.
🔇 Additional comments (4)
src/Example/Example.csproj (1)

14-24: LGTM!

.editorconfig (1)

61-63: LGTM!

AGENTS.md (1)

10-10: LGTM!

Also applies to: 27-35, 62-63

README.md (1)

11-11: LGTM!

Comment thread scripts/rename-placeholders.sh
Comment thread src/Example/FeatureFlags.cs
Comment thread tests/Example.Tests/ExampleClassTests.cs
- Expose EnabledVariant/DisabledVariant as public consts and reference
  them from the test helper (no more hardcoded on/off drift).
- Document that CreateClientAsync re-registers the process-wide provider
  and should be called once at startup.
- Cover FeatureFlags.cs rename+repoint in rename-placeholders.test.sh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Example/FeatureFlags.cs (1)

62-67: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Global provider mutation will race across parallel test classes as more flags are added.

CreateClientAsync re-registers the process-wide Api.Instance provider (documented now), but the scaffold's stated convention (per AGENTS.md) is that every new flag gets its own gated path and its own test class exercising CreateClientAsync. xUnit runs different test classes in parallel by default (only tests within the same class run sequentially), so once a second test class calls CreateClientAsync concurrently with ExampleClassTests, the two classes' provider registrations can interleave and produce nondeterministic assertions — the documentation warns callers, but doesn't prevent this within the test suite itself.

Consider either isolating all FeatureFlags-exercising test classes into a shared [Collection] with parallelization disabled, or documenting this constraint explicitly in AGENTS.md/README.md alongside the "add a test class per flag" guidance so downstream teams don't hit intermittent CI flakiness.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Example/FeatureFlags.cs` around lines 62 - 67, Create a fix for the
process-wide provider mutation in CreateClientAsync by preventing parallel test
classes from interleaving Api.Instance.SetProviderAsync calls. Either group all
FeatureFlags-based test classes, including ExampleClassTests, into a shared
xUnit Collection with parallelization disabled, or add explicit guidance in
AGENTS.md/README.md alongside the per-flag test-class convention so downstream
users know this CreateClientAsync path must not run concurrently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/Example/FeatureFlags.cs`:
- Around line 62-67: Create a fix for the process-wide provider mutation in
CreateClientAsync by preventing parallel test classes from interleaving
Api.Instance.SetProviderAsync calls. Either group all FeatureFlags-based test
classes, including ExampleClassTests, into a shared xUnit Collection with
parallelization disabled, or add explicit guidance in AGENTS.md/README.md
alongside the per-flag test-class convention so downstream users know this
CreateClientAsync path must not run concurrently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7130ff5a-5a17-4101-925f-dc1070f00b51

📥 Commits

Reviewing files that changed from the base of the PR and between c346106 and a152d04.

📒 Files selected for processing (3)
  • scripts/rename-placeholders.test.sh
  • src/Example/FeatureFlags.cs
  • tests/Example.Tests/ExampleClassTests.cs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • devantler-tech/actions (auto-detected)
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Analyze (csharp)
🧰 Additional context used
📓 Path-based instructions (4)
{scripts/rename-placeholders.sh,scripts/rename-placeholders.test.sh}

📄 CodeRabbit inference engine (AGENTS.md)

Keep scripts/rename-placeholders.sh and scripts/rename-placeholders.test.sh in lockstep; changes to placeholder substitutions must be reflected in the test script as well.

Files:

  • scripts/rename-placeholders.test.sh
src/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Call sites must use the FeatureFlags helper and IFeatureClient for feature-flag evaluation; never call the underlying provider directly.

Files:

  • src/Example/FeatureFlags.cs
{src/**/*.cs,tests/**/*.cs}

📄 CodeRabbit inference engine (AGENTS.md)

New features must be gated behind a feature flag by default, and both enabled and disabled states must be covered by tests.

Files:

  • src/Example/FeatureFlags.cs
  • tests/Example.Tests/ExampleClassTests.cs
src/Example/FeatureFlags.cs

📄 CodeRabbit inference engine (AGENTS.md)

Use the scaffold's OpenFeature in-memory provider (FeatureFlags.CreateInMemoryProvider) in the example; do not depend on a real provider or preview-only Microsoft.FeatureManagement bridge packages in the scaffold.

Files:

  • src/Example/FeatureFlags.cs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: devantler-tech/dotnet-template

Timestamp: 2026-07-06T20:21:58.937Z
Learning: Run `dotnet build` and `dotnet test` locally before opening a PR; warnings are treated as errors, so the build must be clean.
Learnt from: CR
Repo: devantler-tech/dotnet-template

Timestamp: 2026-07-06T20:21:58.937Z
Learning: Short-lived release flags must be removed after rollout; only long-lived ops/permission flags are exempt, and trivial/mechanical changes do not need flagging.
🔇 Additional comments (4)
src/Example/FeatureFlags.cs (2)

24-28: LGTM! Both previously flagged concerns (duplicated variant strings, undocumented global provider mutation) are resolved here.

Also applies to: 56-61


1-49: LGTM!

Also applies to: 69-84

tests/Example.Tests/ExampleClassTests.cs (1)

45-116: LGTM! Test coverage satisfies the on/off/unset requirement, and the previously flagged variant-constant duplication is resolved by reusing FeatureFlags.EnabledVariant/DisabledVariant.

scripts/rename-placeholders.test.sh (1)

88-88: LGTM! These assertions match the corresponding subst "src/$new_name/FeatureFlags.cs" call in scripts/rename-placeholders.sh, keeping the two scripts in lockstep as required.

Also applies to: 102-103

CreateClientAsync re-registers the process-wide OpenFeature Api.Instance
provider; xUnit parallelizes across test classes by default, so the
'one test class per flag' convention would race. Put flag-exercising
classes in a shared [Collection("FeatureFlags")] so xUnit runs them
sequentially, and document the convention in AGENTS.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Assistant

Addressed the ⚠️ Outside-diff-range finding (src/Example/FeatureFlags.cs 62-67 — global provider mutation racing across parallel test classes).

Valid concern. Fixed by encoding the safe pattern in the scaffold: flag-exercising test classes now share a [Collection("FeatureFlags")] xUnit collection, so xUnit runs them sequentially and their CreateClientAsync provider registrations can't interleave. ExampleClassTests demonstrates it, and AGENTS.md's Feature flags convention now tells downstream to give every new flag test class the same attribute. Chose the shared-collection route (CR's option 1) over a bare doc note since it prevents the flakiness rather than just warning about it; no [CollectionDefinition] class needed (same collection name already serializes the classes). Build 0-warn, 5 tests green (commit ae3a2e0).

@devantler devantler marked this pull request as ready for review July 6, 2026 21:24
@devantler devantler requested a review from a team as a code owner July 6, 2026 21:24
@devantler devantler merged commit c3dcd50 into main Jul 7, 2026
11 checks passed
@devantler devantler deleted the claude/featureflag-openfeature-scaffold branch July 7, 2026 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: scaffold OpenFeature (+ Microsoft.FeatureManagement provider) feature-flag support

1 participant